﻿/*
    a03_simpleMainLoop -
    本例中演示了main loop, main context, 和gsource的基本使用，


*/
#include <glib.h>
#include <glib/gprintf.h>


static gboolean count_a_cb(gpointer data)
{
    gint *v = data;
    (*v)++;
    g_printf("a=%d\n", *v);
    return G_SOURCE_CONTINUE;
}

static gboolean count_b_cb(gpointer data)
{
    gint *v = data;
    (*v)++;
    g_printf("b=%d\n", *v);
    return G_SOURCE_CONTINUE;
}

//简单的mainloop, 使用timeout source

static G_GNUC_UNUSED void simple_loop(void)
{
    int a;
    int b;
    int c;
    a = b = c = 0;

    GMainContext *ctx;
    GMainLoop *main_loop;
    GSource *source;

    ctx = g_main_context_new();                 //this thread
    main_loop = g_main_loop_new(ctx, FALSE);

    source = g_timeout_source_new(100);
    g_source_set_callback(source, (GSourceFunc)count_a_cb, &a, NULL);
    g_source_attach(source, ctx);
    g_source_unref(source);
    
    source = g_timeout_source_new(200);
    g_source_set_callback(source, (GSourceFunc)count_b_cb, &b, NULL);
    g_source_attach(source, ctx);
    g_source_unref(source);

    source = g_timeout_source_new(5000);
    g_source_set_callback(source, (GSourceFunc)g_main_loop_quit, main_loop, NULL);
    g_source_attach(source, ctx);
    g_source_unref(source);

    g_main_loop_run(main_loop);             //run in this thread
    
    g_printf("a=%d, b=%d \n", a, b);
    g_main_loop_unref(main_loop);
    g_main_context_unref(ctx);
    g_printf("=============   SimpleLoop over ==========\n");
}

///////////////////////////////////////////////////////////////////////////////////////


static GMutex mutex;
static GCond cond;
static gboolean thread_ready = FALSE;

static GThread* main_thread;
static GThread* worker_thread;

static void do_job_a_cb(void)
{
    printf("job_a: thread=%p\n", (gpointer)g_thread_self());
    printf("job_a: by %s thread\n", (worker_thread==g_thread_self()) ? "worker" 
                            : ((main_thread==g_thread_self()) ? "main": "other"));
    g_usleep(50000);    //50ms
}

static void do_job_b_cb(void)
{
    printf("job_b: thread=%p\n", g_thread_self());
    printf("job_b: by %s thread\n", (worker_thread==g_thread_self()) ? "worker" 
                            : ((main_thread==g_thread_self()) ? "main": "other"));
    g_usleep(50000);    //50ms
}

static gpointer worker_thread_func(gpointer data)
{
    GMainContext *ctx = data;
    GMainLoop *loop;

    printf("1: worker is the owner of ctx: %s\n", g_main_context_is_owner(ctx) ? "TRUE" : "FALSE");
    printf("worker thread-default ctx= %p\n", g_main_context_get_thread_default());

    //acquire the ctx, and push it on the thread-default context stack
    g_main_context_push_thread_default (ctx);   //paired with g_main_context_pop_thread_default()

    printf("2:worker is the owner of ctx: %s\n", g_main_context_is_owner(ctx) ? "TRUE" : "FALSE");
    printf("worker thread-default ctx= %p\n", g_main_context_get_thread_default());
    
    loop = g_main_loop_new(ctx, FALSE);

    g_mutex_lock(&mutex);
    thread_ready = TRUE;
    g_cond_signal(&cond);
    g_mutex_unlock(&mutex);

    GSource *source = g_timeout_source_new(1000);
    g_source_set_callback(source, (GSourceFunc)g_main_loop_quit, loop, NULL);
    g_source_attach(source, ctx);
    g_source_unref(source);

    //ctx中调用do_job_a_cb()
    g_main_context_invoke(ctx, (GSourceFunc)do_job_a_cb, NULL);

    g_main_loop_run(loop);
    g_main_context_pop_thread_default (ctx);    
    printf("3:worker is the owner of ctx: %s\n", g_main_context_is_owner(ctx) ? "TRUE" : "FALSE");
    printf("worker thread-default ctx= %p\n", g_main_context_get_thread_default());

    g_main_loop_unref(loop);
    return 0;
}

//worker thread中创建mainloop。
//使用g_main_context_push/pop_thread_default()

static G_GNUC_UNUSED void simple_worker_loop(void)
{
    GMainContext *ctx;
    GThread *thread;

    ctx = g_main_context_new();

    printf("ctx=%p\n", ctx);
    printf("main thread-default ctx= %p\n", g_main_context_get_thread_default());

    printf("main is the owner of ctx: %s\n", g_main_context_is_owner(ctx) ? "TRUE" : "FALSE");
    thread = g_thread_new("worker", worker_thread_func, ctx);
    main_thread = g_thread_self();
    worker_thread = thread;
    printf("main: main=%p, worker=%p\n", main_thread, thread);

    //wait worker ready
    g_mutex_lock (&mutex);
    while (!thread_ready)
        g_cond_wait (&cond, &mutex);
    g_mutex_unlock (&mutex);

    printf("main: woker is ready.\n");
    g_main_context_invoke(ctx, (GSourceFunc)do_job_b_cb, NULL);

    g_thread_join(thread);

    g_printf("=============   SimpleWokerLoop over ==========\n");
}


///////////////////////////////////////////////////////////////////////////////////////


typedef struct
{
    GSource paren;
    guint interval;     //in ms
} SimpleTimerSource;

static gboolean simple_timer_source_prepare (GSource *source, gint *timeout)
{
    printf("timer prepare\n");
    SimpleTimerSource *timer = (SimpleTimerSource*) source;
    *timeout = timer->interval;
    return FALSE;                   //需要check和poll
}

static gboolean simple_timer_source_check(GSource *source)
{
    printf("timer check\n");
    return TRUE;                //继续dispatching
}

static gboolean	simple_timer_source_dispatch (GSource *source, GSourceFunc callback, gpointer user_data)
{
    printf("timer dispatch\n");
    //SimpleTimerSource *timer = (SimpleTimerSource*) source;
    gboolean again;
    if(!callback)
        return G_SOURCE_REMOVE;    //FALSE
    again = callback(user_data);
    return again;
};

GSourceFuncs simple_timer_source_funcs = {
    simple_timer_source_prepare,
    simple_timer_source_check,
    simple_timer_source_dispatch,
    NULL,
    NULL,NULL                       //两个NULL结尾
};


GSource* simple_timer_source_new(guint interval) 
{
    GSource *res = g_source_new(&simple_timer_source_funcs, sizeof(SimpleTimerSource));
    SimpleTimerSource *sts = (SimpleTimerSource*) res;
    sts->interval = interval;

    return res;
}


//验证simpleTimerSource
static G_GNUC_UNUSED void simple_timer_loop(void) 
{
    GMainContext *ctx = g_main_context_default();
    GMainLoop *loop = g_main_loop_new(ctx, FALSE);

    GSource *timer_source = simple_timer_source_new(500);
    int a = 0;
    g_source_set_callback(timer_source, (GSourceFunc) count_a_cb, &a, NULL);
    g_source_attach(timer_source, ctx);     //关联到mainCtx
    g_source_unref(timer_source);

    GSource *quit_source = g_timeout_source_new(3000);
    g_source_set_callback(quit_source, (GSourceFunc)g_main_loop_quit, loop, 0);
    g_source_attach(quit_source, ctx);
    g_source_unref(quit_source);

    g_main_loop_run(loop);
    g_main_loop_unref(loop);
    g_printf("=============   SimpleTimerLoop over ==========\n");    
}


///////////////////////////////////////////////////////////////////////////////////////


int main(int argc, char** argv)
{
#if 0    
    simple_loop();
#endif
#if 0    
    simple_worker_loop();
#endif    
#if 1
    simple_timer_loop();
#endif
    return 0;
}

